/*
 * @Author: xiaosihan 
 * @Date: 2023-12-10 04:40:28 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-01-07 18:37:05
 */

import editor3DStore from "@views/editor3D/editor3DStore";
import { uniq } from "lodash";
import { autorun, toJS } from "mobx";
import { DoubleSide, Euler, Group, InstancedBufferAttribute, InstancedMesh, Matrix4, MeshBasicMaterial, MeshLambertMaterial, Quaternion, Vector3 } from "three";
import threeUtils from "three-base/threeUtils";
import MeshBase from "../MeshBase/MeshBase";
import InstancedGroupMesh from "../InstancedGroupMesh";
import ModelEventBox from "./ModelEventBox";
import { degToRad } from "three/src/math/MathUtils";

// 模型组
class ModelGroup extends Group {
    constructor() {
        super();
    }

    static _position: Vector3 = new Vector3();
    static _scale: Vector3 = new Vector3(1, 1, 1);
    static _rotation: Euler = new Euler();
    static _Quaternion: Quaternion = new Quaternion();
    static _Matrix4: Matrix4 = new Matrix4();

    //模型点击盒子
    modelEventBox = (() => {
        const modelEventBox = new ModelEventBox();

        //three事件
        modelEventBox.userData = {
            cursor: "pointer",
            enableEvent: true,
        };

        //根据状态控制鼠标事件
        autorun(() => {
            const { state } = editor3DStore;
            // 添加模型时,事件盒子关闭鼠标事件
            Object.assign(modelEventBox.userData, { enableEvent: state === "" });
        });

        modelEventBox.addEventListener("click", (e: any) => {
            const modelDatas = editor3DStore.getModelDatas();
            const modelData = modelDatas[e.instanceId];
            const matrix = new Matrix4();
            modelEventBox.getMatrixAt(0, matrix);
            const scaleY = matrix.elements[5]; // 获取y缩放值
            editor3DStore.setActiveId(modelData.id, scaleY);
        });

        this.add(modelEventBox);
        return modelEventBox;
    })();


    //实例模型组
    group = (() => {
        const group = new class InstanceGroup extends Group {
            constructor() {
                super();
            }
            //实例模型组
            declare children: Array<InstancedGroupMesh>;
        }();

        this.add(group);
        return group;
    })();

    dispose = autorun(async () => {
        const { activeId, activeFloorId, inFloorId } = editor3DStore;

        //不渲染当前选中的模型
        const modelDatas = toJS(editor3DStore.modelDatas);

        //按照url 分组
        const urls = uniq(modelDatas.map(d => d.url));

        //先全部隐藏
        this.group.children.find(o => o.visible = false);

        // 遍历 url
        for (let url of urls) {
            const modelDatasWithUrl = modelDatas.filter(d => d.url === url);

            // 按照 url来获取 InstanceMeshGroup 对象
            const instanceMeshGroup = await this.getInstanceMeshGroupByUrl(url);
            instanceMeshGroup.visible = true; // 用到的就显示出来

            for (let i = 0; i < modelDatasWithUrl.length; i++) {

                // 如果实例数量不对 就需要创建新的实例矩阵
                if (modelDatasWithUrl.length !== instanceMeshGroup.count) {
                    instanceMeshGroup.count = modelDatasWithUrl.length;
                    instanceMeshGroup.children.map((instanceMesh: InstancedMesh) => {
                        instanceMesh.instanceMatrix = new InstancedBufferAttribute(new Float32Array(modelDatasWithUrl.length * 16), 16);
                        instanceMesh.count = instanceMeshGroup.count;
                    });
                }

                // 设置矩阵
                ModelGroup._position.set(modelDatasWithUrl[i].positionX, modelDatasWithUrl[i].positionY, modelDatasWithUrl[i].positionZ);
                ModelGroup._rotation.set(
                    degToRad(modelDatasWithUrl[i].heading),
                    degToRad(modelDatasWithUrl[i].pitch),
                    degToRad(modelDatasWithUrl[i].roll)
                );
                ModelGroup._Quaternion.setFromEuler(ModelGroup._rotation);

                // 只显示当前进入的那层楼的模型
                const sclae = (modelDatasWithUrl[i].floorId === inFloorId) ? 1 : 0;
                ModelGroup._scale.set(sclae, sclae, sclae);

                ModelGroup._Matrix4.compose(ModelGroup._position, ModelGroup._Quaternion, ModelGroup._scale);
                instanceMeshGroup.setMatrixAt(i, ModelGroup._Matrix4);

            }
        }


        // 创建点击模型盒的实例矩阵
        if (modelDatas.length !== this.modelEventBox.count) {
            this.modelEventBox.count = modelDatas.length;
            this.modelEventBox.instanceMatrix = new InstancedBufferAttribute(new Float32Array(modelDatas.length * 16), 16);
        }

        //设置点击模型盒的矩阵
        for (let i = 0; i < modelDatas.length; i++) {
            // 设置矩阵
            ModelGroup._position.set(modelDatas[i].positionX, modelDatas[i].positionY, modelDatas[i].positionZ);
            const instanceMeshGroup = await this.getInstanceMeshGroupByUrl(modelDatas[i].url);

            // 只显示当前进入的那层楼的模型
            if (modelDatas[i].floorId === inFloorId) {
                ModelGroup._scale.copy(instanceMeshGroup.size);
            } else {
                ModelGroup._scale.set(0, 0, 0);
            }

            ModelGroup._rotation.set(
                degToRad(modelDatas[i].heading),
                degToRad(modelDatas[i].pitch),
                degToRad(modelDatas[i].roll)
            );
            ModelGroup._Quaternion.setFromEuler(ModelGroup._rotation);

            ModelGroup._Matrix4.compose(ModelGroup._position, ModelGroup._Quaternion, ModelGroup._scale);
            this.modelEventBox.setMatrixAt(i, ModelGroup._Matrix4);
        }
        this.modelEventBox.instanceMatrix.needsUpdate = true;
        this.modelEventBox.computeBoundingSphere();

    });


    //根据url 获取实例模型组  url
    async getInstanceMeshGroupByUrl(url: string): Promise<InstancedGroupMesh> {
        let instanceMeshGroup = this.group.children.find(o => o.url === url);
        if (!instanceMeshGroup) {

            const meshBase = new MeshBase(url);
            await new Promise<void>((resolve, reject) => {
                meshBase.addEventListener("loaded", () => {
                    meshBase.traverseMesh(mesh => {
                        const { color, map, transparent, opacity, alphaTest, side } = mesh.material as MeshBasicMaterial;
                        mesh.material = new MeshLambertMaterial({ color, map, transparent, opacity, alphaTest: 0.2, side });
                        if (!mesh.geometry.getAttribute("normal")) {
                            mesh.geometry.computeVertexNormals();
                        }
                        //     (mesh.material as MeshBasicMaterial).depthTest = true;
                        //     (mesh.material as MeshBasicMaterial).depthWrite = true;
                        //     (mesh.material as MeshBasicMaterial).transparent = false;
                        //     (mesh.material as MeshBasicMaterial).alphaTest = 0.2;
                    });
                    resolve();
                });
            });

            threeUtils.center(meshBase);
            threeUtils.alignBottom(meshBase);

            instanceMeshGroup = new InstancedGroupMesh(meshBase, 0);
            instanceMeshGroup.url = url;

            // 有就不重复添加 实例模型组对象
            if (!this.group.children.some(f => f.url === url)) {
                this.group.add(instanceMeshGroup);
            }

        }
        return instanceMeshGroup;
    }
    //

}

const modelGroup = new ModelGroup();

export default modelGroup;